from django.shortcuts import render
from django.views import View
from django.http import JsonResponse
from django.utils import timezone
from django_redis import get_redis_connection

from meiduo_mall.utils.views import LoginRequiredJsonMixin

from goods.models import SKU
from users.models import Address
from .models import OrderInfo,OrderGoods

import json
from decimal import Decimal
# Create your views here.


# 获取结算数据
# 继承LoginRequiredJsonMixin，表示当前视图必须登陆才能访问
class OrderSettlementView(LoginRequiredJsonMixin, View):

    def get(self, request):
        user = request.user # 必须是登陆的User对象

        # 1、购物车数据
        skus = []

        conn = get_redis_connection('carts')
        # redis_carts = {b'11':b'5',...}
        redis_carts = conn.hgetall('carts_%s'%user.id)
        # redis_selected = [b'11']
        redis_selected = conn.smembers('selected_%s'%user.id)

        sku_ids = redis_carts.keys()
        for sku_id in sku_ids:
            if sku_id in redis_selected: # 选中
                sku = SKU.objects.get(pk=sku_id)
                skus.append({
                    'id': sku.id,
                    'name': sku.name,
                    'default_image_url': sku.default_image.url,
                    'count': int(redis_carts[sku_id]), # int(b'11') --> 11
                    'price': sku.price
                })


        # 2、收货地址数据
        addresses = []
        # 过滤出当前登陆用户可选的收货地址(没有逻辑删除的地址)
        user_addresses = Address.objects.filter(user=user, is_deleted=False)
        for user_addr in user_addresses:
            # user_addr一个收货地址对象
            addresses.append({
                'id': user_addr.id,
                'province': user_addr.province.name,
                'city': user_addr.city.name,
                'district': user_addr.district.name,
                'place': user_addr.place,
                'mobile': user_addr.mobile,
                'receiver': user_addr.receiver
            })

        # 运费
        freight = 10.0

        return JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'context': {
                'addresses': addresses,
                'skus': skus,
                'freight': freight
            }
        })


from django.db import transaction
# 新建订单
class OrderCommitView(LoginRequiredJsonMixin, View):

    def post(self, request):

        data = json.loads(request.body.decode())
        address_id = data.get('address_id')
        pay_method = data.get('pay_method')

        if not all([address_id,  pay_method]):
            return JsonResponse({'code': 400, 'errmsg': '缺少必要字段'})

        try:
            addr = Address.objects.get(pk=address_id)
        except Address.DoesNotExist as e:
            return JsonResponse({'code': 400, 'errmsg': '地址不存在'})

        if pay_method not in [
            OrderInfo.PAY_METHODS_ENUM['CASH'], # 1
            OrderInfo.PAY_METHODS_ENUM['ALIPAY'], # 2
        ]:
            return JsonResponse({'code': 400, 'errmsg': '支付方式不支持'})

        user = request.user
        # 读取redis购物策车中的sku商品
        conn = get_redis_connection('carts')
        # redis_carts = {b'11':b'5',...}
        redis_carts = conn.hgetall('carts_%s' % user.id)
        # redis_selected = [b'11']
        redis_selected = conn.smembers('selected_%s' % user.id)
        # 统一将redis购物车数据组织成购物车字典数据方便后续操作
        cart_dict = {}
        for k,v in redis_carts.items():
            sku_id = int(k)
            count = int(v)
            if k in redis_selected: # 保证购物车字典数据里面都是选中的商品
                cart_dict[sku_id] = {
                    'count': count,
                    'selected': k in redis_selected
                }

        # 使用with语法启动视图，在with代码块内部的所有模型类增删改查都在一个事物中执行
        with transaction.atomic():
            # 设置一个事物回滚点
            save_id = transaction.savepoint()

            # 1、新建订单OrderInfo
            order_id = timezone.localtime().strftime('%Y%m%d%H%M%S') + '%06d'%user.id
            # order就是我们新建的订单对象
            order = OrderInfo.objects.create(
                order_id=order_id,
                user=user,
                address_id=address_id,
                total_count=0,
                total_amount=0,
                freight=Decimal('10.0'), # 表示十进制浮点数10.0
                pay_method=pay_method,
                status= OrderInfo.ORDER_STATUS_ENUM['UNSEND'] if pay_method==OrderInfo.PAY_METHODS_ENUM['CASH'] else OrderInfo.ORDER_STATUS_ENUM['UNPAID']
            )

            # 2、新建订单商品OrderGoods
            sku_ids = cart_dict.keys()
            for sku_id in sku_ids:

                while True:
                    # TODO: 乐观锁step1 —— 读取旧数据
                    sku = SKU.objects.get(pk=sku_id)
                    old_stock = sku.stock # 库存
                    old_sales = sku.sales # 销量

                    # 判断库存
                    count = cart_dict[sku_id]['count'] # 下单数量
                    if count > old_stock: # 下单数量大于库存
                        # 由于库存不足，撤销sql执行，回滚到保存点
                        transaction.savepoint_rollback(save_id)
                        return JsonResponse({'code': 400, 'errmsg': '库存不足'})

                    # TODO: 乐观锁step2 —— 基于旧数据基础上计算新值（计算的过程中可能比较耗时）
                    new_stock = old_stock - count
                    new_sales = old_sales + count

                    # TODO: 乐观锁step3 —— 根据旧数据查询并修改
                    # 查询集update函数返回一个整数，表示更新成功的条数
                    result = SKU.objects.filter(
                        pk=sku_id, stock=old_stock, sales=old_sales # 根据旧数据查询
                    ).update(
                        stock=new_stock, sales=new_sales # 更新新数据
                    )

                    if result:
                        # 1表示有一条数据成功修改 --> 根据就数据查询到了原有的模型类对象 --> 旧数据没有发生改变，期间没有别的数据介入修改
                        break
                    # 0表示没有数据被更新成功 --> 过滤出来的查询集为空 --> 根据旧数据查询不到 --> 旧数据发生改变，期间有别的事务介入修改了
                    continue


                # 新建中间表数据OrderGoods
                OrderGoods.objects.create(
                    order=order,
                    sku=sku,
                    count=count,
                    price=sku.price
                )

                # 累加计算订单的总数量和总价格
                order.total_count += count
                order.total_amount += sku.price * count

            order.total_amount += order.freight
            order.save()

            # 清除保存点
            transaction.savepoint_commit(save_id)

        # 清除购物车中已下单的商品
        sku_ids = cart_dict.keys()
        conn.hdel('carts_%s'%user.id, *sku_ids) # 删除redis购物车中所有已经下单的商品
        conn.srem('selected_%s'%user.id, *sku_ids) # 删除redis集合中所有已下单的商品

        return JsonResponse({'code': 0, 'errmsg': 'ok', 'order_id': order_id})












